package com.esdk.sql;

import com.esdk.esdk;
import com.esdk.sql.orm.IRow;
import com.esdk.sql.orm.ParentRow;
import com.esdk.utils.CharAppender;
import lombok.NonNull;

import java.sql.Connection;
import java.util.*;

/**
 * @author ffychina@163.com
 * @since 2024-06-23
 * 通过这种方式实现批量update，暂时只实现了mysql
 * INSERT INTO table_name (column1, column2)
 * VALUES (value1, value2)
 * ON DUPLICATE KEY UPDATE column1 = value1, column2 = value2;
 * 注意：values值不支持表达式，如字段自增，如需要表达式赋值建议用BatchMultiUpdate类。
 * */
public class BatchUpdate extends BatchReplace{
  private LinkedHashSet<String> columns=new LinkedHashSet<>();
  public BatchUpdate(String tablename,Connection conn){
    super(tablename,conn);
  }

  @Override
  public void parse(){
    replaceSQL=parseBatchUpdate(tableName,rowList,ignoreRowChanged);
  }

  @Override
  public void clear(){
    super.clear();
    columns.clear();
  }

  private Collection<String> getRequiredColumnNames(@NonNull Object mdObj){
    boolean[] nullables=(boolean[])esdk.reflect.getFieldValue(mdObj,"isNullables");
    String[] columnNames=(String[])esdk.reflect.getFieldValue(mdObj,"FieldNames");
    HashSet<String> result=new HashSet();
    for(int i=0;i<nullables.length;i++){
      if(nullables[i]==false)
        result.add(columnNames[i]);
    }
    return result;
  }

  /**注意：以所有记录的修改字段并集作为更新字段*/
  public String parseBatchUpdate(String tableName,List<IRow> rowlist,boolean ignoreRowChanged){
    String result="";
    if(!rowlist.isEmpty()){
      IRow first=rowList.getFirst();
      String primaryKeyName=first instanceof ParentRow parentRow?parentRow.getPrimaryKeyName():first.getColumns()[0];//目前只考虑首字段就是id字段的情况
      columns.addFirst(primaryKeyName);
      for(IRow row: rowlist){
        columns.addAll(row.getChanged().keySet());
      }
      if(first instanceof ParentRow parentRow){
        columns.addAll(getRequiredColumnNames(parentRow.getMetaData()));
      }
      String values=parseValues(rowlist);
      CharAppender updateCols=new CharAppender(',');
      for(String column: columns){
        updateCols.add(column+"=VALUES("+column+")");
      }
      result="INSERT INTO "+tableName+" ("+esdk.str.join(',',columns.toArray())+") VALUES "+values+" ON DUPLICATE KEY UPDATE "+updateCols;
    }
    return result;
  }

  private String parseValues(List<IRow> list){
    CharAppender allValues=new CharAppender(',');
    for(IRow row: list){
      CharAppender oneValues=new CharAppender(',');
      for(String col:columns){
        oneValues.append(SQLAssistant.getStmtSqlValue(row.get(col)));
      }
      allValues.append("("+oneValues+")");
    }
    return allValues.toString();
  }

  public BatchUpdate addValues(IRow row){
    super.addValues(row);
    return this;
  }

  public BatchUpdate addValues(ParentRow row){
    super.addValues(row);
    return this;
  }

  public String getEllipseSql() {
    StringBuilder result=new StringBuilder();
    if(!rowList.isEmpty()) {
      result.append(parseBatchUpdate(tableName,rowList.subList(0,1),ignoreRowChanged));
    }
    int index=result.indexOf(" ON DUPLICATE KEY UPDATE");
    result.insert(index,"...");
    return result.toString();
  }
}
